package org.lt.cj.core;

import java.util.List;
import org.lt.cj.config.entities.ConfigModel;
import org.lt.cj.config.util.RegCheck;
import org.lt.cj.core.filters.EntityFilter;
import org.lt.cj.core.filters.LinkFilterImpl;
import org.lt.cj.core.utils.ExtractLinksThread;
import org.lt.cj.core.utils.FiltDataThread;
import org.lt.cj.model.dao.ICollectDao;
import org.lt.cj.model.impl.CollectImpl;

public class Crawler {

    private LinkDB linkDB;
    private ConfigModel configModel;
    private EntityFilter entityFilter;
    private boolean flag = true;
    private boolean suspendFlag;
    private boolean stopFlag;

    public Crawler(LinkDB linkDB, ConfigModel configModel) {

        this.configModel = configModel;
        this.linkDB = linkDB;
        suspendFlag = false;
        stopFlag = false;
    }

    /* 使用种子 url 初始化 URL 队列*/
    private void initCrawlerWithSeeds(List<Seed> seeds) {
        for (int i = 0; i < seeds.size(); i++) {
            linkDB.addUnvisitedSeed(seeds.get(i));
        }
    }

    public String stopCrawling() {
        this.stopFlag = true;
        return "";
    }

    public Seed getSeed() {

        Seed seed = null;
        synchronized (linkDB) {
            //如果未访问的URL队列内url数量为零的话，停止爬虫程序
            if (!linkDB.unVisitedUrlsEmpty()) {
                seed = linkDB.unvisitedSeedDeQueue();                        //没访问过的的链接链表出队
            } else {
                flag = false;
            }
        }
        //将下面的判断语句移出了上面的同步代码块
        if (seed != null && !seed.getUrl().equals("")) {
            linkDB.addVisitedUrl(configModel.getTaskName(), seed.getUrl());          //该 url 放入到已访问的 URL 中 to check
        }
        return seed;
    }

    public void crawl() {
        //队头Seed出队
        Seed visitSeed = null;
        List<Seed> links = null;
        synchronized (linkDB) {
            visitSeed = this.getSeed();
            LinkFilter filter = new LinkFilterImpl(configModel);                //定义一个链接过滤器
            links = new HtmlParserTool(configModel).fetchSeeds(visitSeed, filter);//提取出下载网页中的 URL
        }
        //新的未访问的 URL 入队
        if (links != null) {
            for (Seed link : links) {
                linkDB.addUnvisitedSeed(link);
            }
        }
        filtData(visitSeed);
    }

    public void crawl2() {
        //队头Seed出队
        Seed visitSeed = null;
        List<Seed> links = null;
        synchronized (linkDB) {
            visitSeed = getSeed();
            LinkFilter filter = new LinkFilterImpl(configModel);                //定义一个链接过滤器
            links = new HtmlParserTool(configModel).fetchSeeds(visitSeed, filter);//提取出下载网页中的 URL
            //新的未访问的 URL 入队
            if (links != null) {
                for (Seed link : links) {
                    linkDB.addUnvisitedSeed(link);
                }
            }
        }
        filtData(visitSeed);
    }

    public void startCrawling() {

        ICollectDao dao = new CollectImpl();
        //队头 URL 出对
        Seed visitSeed = null;
        List<Seed> links = null;
        //循环条件：待抓取的链接不空
        while (flag && !stopFlag) {
            try {
                synchronized (this) {
                    if (suspendFlag) {
                        wait();
                    }
                }
                links = null;
                int getCountFromDB = 500;
                //控制采集到的未访问的URL队列的URL数量
                //如果内存内未访问的链接数量超过了设定的值，那么将这些URL存入数据库先，内存URL队列内数量不够的再取出来使用
                //如果内存内未访问的链接数量没超过设定的值，那么从链接队列出队获得新入口去取得更多的链接入口
                boolean exceedFlag = true;
                synchronized (linkDB) {
                    if (linkDB.getUnVisitedUrl().getCount() < 300) { //to do 原来是600
                        exceedFlag = false;
                        //to do 从数据库取出一些链接加入到Url队列
//                        links = dao.getUnvisitedURLs(configModel.getTaskName(), getCountFromDB);
                        links = dao.getUnvisitedSeeds(configModel.getTaskName(), getCountFromDB);
                        //  to do这里用了同步后慢了很多，不用的话会报出一个contains的异常,现在解除同步
                        if (links != null) {
                            for (Seed link : links) {
                                linkDB.addUnvisitedSeed(link);//如果数据库内的记录大于，且内存内的记录数量不超过指定的数量的话就直接将数据库内的记录插入内存队列
                            }
                            if (links.isEmpty()) {
                                crawl2();
                            }
                        } else if (links == null) {
                            crawl2();
                        }
                    } else {
                        //检查unvisitedUrl表里有没有这么多数据，没有则再通过新链接去获取更多链接，并将这些链接存入数据库
                        visitSeed = this.getSeed();
                    }
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                }
                //检查unvisitedUrl表里是否有指定数量的数据，而且内存队列内URL数量没有超过指定数量，符合条件的话则直接从数据库取出放到内存队列里
                if (dao.getUnvisitCountByTask(configModel.getTaskName()) > getCountFromDB && !exceedFlag) {

                    if (!flag) {//已经运行完毕了
                        return;
                    }
                    links = dao.getUnvisitedSeeds(configModel.getTaskName(), getCountFromDB);
                    for (Seed link : links) {
                        linkDB.addUnvisitedSeedUrl(link);//如果数据库内的记录大于，且内存内的记录数量不超过指定的数量的话就直接将数据库内的记录插入内存队列
                    }
                } else if (dao.getUnvisitCountByTask(configModel.getTaskName()) < 400 && !exceedFlag) {
                    crawl();
                } else if (exceedFlag) {//如果内存内的记录超过了指定的数量，那么将一直使用出列操作，新采集到的URL将存入数据库
                    insertNewLinks(linkDB, configModel, visitSeed);//启动线程后台去插新链接到数据库
                    filtData(visitSeed);  //过滤数据
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    public void insertNewLinks(LinkDB linkDB, ConfigModel configModel, Seed visitSeed) {

        Thread extraLinksThread = new Thread(new ExtractLinksThread(linkDB, configModel, visitSeed));
        extraLinksThread.start();
    }

    public void filtData(Seed visitSeed) {

        if (visitSeed == null) {
            return;
        }
        //先匹配判断下这个是不是该进入过滤的页面链接
        RegCheck util = RegCheck.getInstance();
        if (!util.checkUrl(visitSeed.getUrl(), configModel.getPageReg())) {
            return;
        }
        //启动过滤数据线程
        Thread filtThread = new Thread(new FiltDataThread(configModel, visitSeed));
        filtThread.start();
    }

    public boolean isStopFlag() {
        return stopFlag;
    }

    public void setStopFlag(boolean stopFlag) {
        this.stopFlag = stopFlag;
    }

    public boolean isSuspendFlag() {
        return suspendFlag;
    }

    public void setSuspendFlag(boolean suspendFlag) {
        this.suspendFlag = suspendFlag;
    }

    public synchronized void resume() {
        suspendFlag = false;
        notify();
    }
}
